/*   The MIT License
 *   
 *   Carina Common
 *   Copyright (c) 2010-2011 Zdravko Velinov
 *   
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */

#ifndef PATTERNS_HH_
#define PATTERNS_HH_

#include "carina/common/global.hh"
#include "carina/common/exception.hh"
#include "carina/common/assert.hh"

#include <cassert>
#include <unordered_map>

#include <functional>

using std::function;

namespace Carina
{
/*! \brief Common template for creating an object that has a single instance within the application.
 * 
 *  Singletons are objects that have just a single instance within the application and they are shared
 *  between multiple objects. Essentially, they serve the purpose of global variables with a little bit
 *  better memory management. In any case, you are discouraged to use them because they introduce hidden
 *  dependencies between objects. Also, it is awfully hard in a later stage to introduce multi-threading
 *  in your application when everything depends on a single object. That's mostly because locking a mutex
 *  is not for free, even if there are not any other objects accessing the memory.
 * 
 *  \tparam T   the type of the object that must have a single instance.
 */
template<class T>
class Singleton
{
    static T* m_Instance; /*! \brief Pointer to the single instance of the specified object type.
                           *  
                           *  Some people might look at this and say to themselves that it would be
                           *  much more clever to declare it in the global space in some file. The thing
                           *  is, that it turns the code into complete spaghetti at some point because
                           *  some people start to recognize it as legit pattern to carry around state.
                           */
public:
    //! Constructor.
    Singleton()
    {
		CE_ASSERT(m_Instance == nullptr, "Singleton not initialized"); m_Instance = (T*)this;
    }

    //! Destructor.
	~Singleton() { CE_ASSERT(m_Instance, "Singleton not initialized or manually freed"); m_Instance = nullptr; }

    /*! \brief Gets a reference to the singleton object.
     *  \remarks You might as well wrap it behind some function. No one needs to know about our awful design.
     */
    static T& getSingleton() 
    { 
        if(!m_Instance)
            THROW_EXCEPTION("Empty singleton");
        return *m_Instance; 
    }
    
    /*! \brief Gets a pointer to the singleton object.
     *  \remarks You might as well wrap it behind some function. No one needs to know about our awful design.
     */
    
    static T* getSingletonPtr() { CE_ASSERT(m_Instance, "Singleton not initialized"); return m_Instance; }
};

template<class T> T* Singleton<T>::m_Instance = nullptr;

//! \deprecated Legacy code, should be dropped and replaced with "= delete" declarations.
class non_copyable
{
    non_copyable(const non_copyable&);
    non_copyable& operator=(const non_copyable&);
protected:
    non_copyable() {}
    ~non_copyable() {}
};

template<class T, class TDeleter> class ScopedObject;

// TODO: Replace function with lambda. It might be faster.
template<class T, class TDeleter>
class ScopedObject<T*, TDeleter>
{
    T*                  m_Ptr;
    TDeleter            m_Deleter;
public:
    ScopedObject() {}
    
    ScopedObject(TDeleter deleter)
        :   m_Deleter(deleter) {}
        
     ~ScopedObject() { if(m_Ptr) m_Deleter(m_Ptr); }

    ScopedObject& operator=(T* t) { m_Ptr = t; return *this; }

    T* get() { return m_Ptr; }
    const T* get() const { return m_Ptr; }

    T** operator&(){ return &m_Ptr; }
    operator T*() const { return m_Ptr; }
    T& operator*() { return *m_Ptr; }
    T* operator->() { return m_Ptr; }

    T* const * operator&() const { return &m_Ptr; }
    const T& operator*() const { return *m_Ptr; }
    const T* operator->() const { return m_Ptr; }
};

template<class TRollback>
class Transaction
{
    bool                   m_Status;
    TRollback              m_Rollback;
public:
    Transaction(TRollback _rollback)
        :   m_Status(true),
            m_Rollback(_rollback) {}
        
    ~Transaction() { if(m_Status) m_Rollback(); }
    
    void commit() { m_Status = false; }
};

template<class TFunc>
Transaction<TFunc> CreateTransaction(TFunc func)
{
    return Transaction<TFunc>(func);
}

template<class TFunc>
class AtScopeExit
{
    TFunc                  m_Func; 
public:
    AtScopeExit(TFunc func)
        :   m_Func(func) {}
    ~AtScopeExit() { m_Func(); }
};

template<class TFunc>
AtScopeExit<TFunc> CreateAtScopeExit(TFunc func)
{
    return AtScopeExit<TFunc>(func);
}

template<typename T, typename TDeleter>
ScopedObject<T, TDeleter> CreateScoped(TDeleter deleter) { return ScopedObject<T, TDeleter>(deleter); }

#define CREATE_SCOPED(_type, _func) CreateScoped<_type>([](_type t) { _func(t); })

template<class T>
class Factory
{
public:
    virtual ~Factory() {}

    virtual T create()=0;
};

template<class T, class TFactory = Factory<T> >
class FactoryManagerBase
{
public:
    typedef shared_ptr< TFactory >                  FactoryPtr;
    typedef std::unordered_map< string,
                                FactoryPtr >        FactoryMap;
    typedef typename FactoryMap::iterator           iterator;
    typedef typename FactoryMap::const_iterator     const_iterator;
private:
    FactoryMap                                      m_Factories;
public:
    void registerFactory(const string& name, const FactoryPtr& ptr)
    {
        m_Factories[name] = ptr;
    }

    void unregisterFactory(const string& name)
    {
        iterator iter = getFactory(name);
        if(iter == m_Factories.end())
            THROW_EXCEPTION("Unknown factory: " + name);
        m_Factories.erase(iter);
    }

    iterator getFactory(const string& name)
    {
        return m_Factories.find(name);
    }

    const_iterator getFactory(const string& name) const
    {
        return m_Factories.find(name);
    }

    const_iterator getFactoryBegin() const
    {
        return m_Factories.begin();
    }

    const_iterator getFactoryEnd() const
    {
        return m_Factories.end();
    }
};

template<class T>
class FactoryManager: public FactoryManagerBase<T>
{
public:
    typedef typename FactoryManagerBase<T>::iterator            iterator;
    typedef typename FactoryManagerBase<T>::const_iterator      const_iterator;

    T create(const string& name)
    {
        iterator iter = this->getFactory(name);
        if(iter == this->getFactoryEnd())
            THROW_EXCEPTION("Unknown factory: " + name);
        return iter->second->create();
    }
};
}

#endif /* PATTERNS_HH_ */
